package net.mingsoft.report_data.biz.impl.report_gen;

import com.alibaba.fastjson.JSON;
import net.mingsoft.basic.util.DateUtil;
import net.mingsoft.report_data.biz.ZkDataReportPopulationAnalysisBiz;
import net.mingsoft.report_data.biz.ZkDataSgSqBiz;
import net.mingsoft.report_data.biz.impl.r.*;
import net.mingsoft.report_data.constant.ZkDataConstant;
import net.mingsoft.report_data.dao.ZkDataDao;
import net.mingsoft.report_data.entity.*;
import net.mingsoft.report_data.entity.vo.DateWithCountDto;
import net.mingsoft.report_data.enums.HainanAreaUtil;
import net.mingsoft.report_data.enums.PeriodEnum;
import net.mingsoft.report_data.enums.ReportPeriodType;
import net.mingsoft.report_data.kafka.entity.TravelAreaDto;
import net.mingsoft.report_data.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.joda.time.DateTime;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author gengfa
 * @date 22/03/14
 */
@Service("DYNAMIC_POPULATION")
public class ZkDataReportPopulationAnalysisBizImpl implements ZkDataReportPopulationAnalysisBiz {
    @Resource
    FreeMarkerConfigurer configurer;

    @Resource
    ZkDataDao dataDao;

    @Resource
    ZkDataSgSqBiz sgSqBiz;

    private static final BigDecimal HUNDRED_DECIMAL = BigDecimal.valueOf(100);
    private static final String TOTAL_FIELD_NAME = "总计";

//    @Override
//    public String generate(PeriodEnum periodEnum) {
//        Map<String, Object> map = new HashMap<>(16);
//
//        String[] dateRange = PeriodUtil.getDateRange(ReportPeriodType.THIS_WEEK);
//        map.put("beginDate", dateRange[0]);
//        map.put("endDate", dateRange[1]);
//
//        // 指标1：常住人口增长数据
//        getPermanentPopulationData(periodEnum, map);
//
//        // 指标2：流动人口数据
//        getFlowPopulation(map);
//
//        // 指标3：国外人口数据
//        long total = getForeignPopulation(map);
//        getForeignPopulationByLocalCity(total, map);
//
//        // 指标4：人口地理分布
//        getPopulationDistributed(map);
//
//        // 指标5：走私人员出行OD分析
//        getSmugglerOdTimesData(map);
//        getSmugglerOdPeriodData(map);
//        getSmugglerOdDistanceData(map);
//        getSmugglerOdArriveAreaData(map);
//        getSmugglerWorkHomeData(map);
//
//        // 指标6：沿海岸线人口分布分析
//        List<CoastalAreaResponse> data = getCoastalWorkHomePopulationData(map);
//        getCoastalActiveAreaData(data, map);
//
//        // 指标7: 城市人口迁徙
//        getCityMigrationData(map);
//
//        return ReportFtlRender.render(configurer, "report/form-rkdtbhfx.ftl", map);
//    }
    private static final Map<String, Function<Map<String, Object>, Map<String, Object>>> METRIC_MAP = new HashMap<>(16);

    @Override
    public String generate(ReportPeriodType periodType) {
        // 动态人口报告分析
        METRIC_MAP.put("dp_flow", this::getFlowPopulation);
        METRIC_MAP.put("dp_foreign", m -> getForeignPopulationByLocalCity(getForeignPopulation(m), m));
        METRIC_MAP.put("dp_area", this::getPopulationDistributed);
        METRIC_MAP.put("dp_smuggler_od", m -> {
            getSmugglerOdTimesData(m);
            getSmugglerOdPeriodData(m);
            getSmugglerOdDistanceData(m);
            getSmugglerOdArriveAreaData(m);
            getSmugglerWorkHomeData(m);
            return m;
        });
        METRIC_MAP.put("dp_coastal", m -> getCoastalActiveAreaData(getCoastalWorkHomePopulationData(m), m));
        METRIC_MAP.put("dp_city_migrate", this::getCityMigrationData);

        METRIC_MAP.put("dp_realtime_user", this::getRealTimeUser);
        METRIC_MAP.put("dp_in_out_island", this::getInOutIslandUser);
//        METRIC_MAP.put("dp_in_out_island", m -> m);
        METRIC_MAP.put("dp_pop_structure", this::getPopulationStructure);
//        METRIC_MAP.put("dp_pop_structure", m -> m);
        METRIC_MAP.put("dp_area_job_structure", this::getAreaJobStructure);

        Map<String, Object> map = new HashMap<>(16);

        String[] dateRange = PeriodUtil.getDateRange(periodType);
        map.put("beginDate", dateRange[0]);
        map.put("endDate", dateRange[1]);

        PeriodEnum periodEnum;
        if (periodType.equals(ReportPeriodType.LAST_WEEK)) {
            periodEnum = PeriodEnum.WEEK;
        } else if (periodType.equals(ReportPeriodType.LAST_MONTH)) {
            periodEnum = PeriodEnum.MONTH;
        } else if (periodType.equals(ReportPeriodType.LAST_SEASON)) {
            periodEnum = PeriodEnum.SEASON;
        } else if (periodType.equals(ReportPeriodType.LATTER_HALF_LAST_YEAR)) {
            periodEnum = PeriodEnum.HALF_YEAR;
        } else if (periodType.equals(ReportPeriodType.LAST_YEAR)) {
            periodEnum = PeriodEnum.YEAR;
        } else {
            periodEnum = PeriodEnum.WEEK;
        }
        METRIC_MAP.put("dp_permanent", m -> getPermanentPopulationData(periodEnum, m));
        /*// dp_permanent
        // 指标1：常住人口增长数据
        getPermanentPopulationData(periodEnum, map);

        // dp_flow
        // 指标2：流动人口数据
        getFlowPopulation(map);

        // dp_foreign
        // 指标3：国外人口数据
        long total = getForeignPopulation(map);
        getForeignPopulationByLocalCity(total, map);

        // dp_area
        // 指标4：人口地理分布
        getPopulationDistributed(map);

        // dp_smuggler_od
        // 指标5：走私人员出行OD分析
        getSmugglerOdTimesData(map);
        getSmugglerOdPeriodData(map);
        getSmugglerOdDistanceData(map);
        getSmugglerOdArriveAreaData(map);
        getSmugglerWorkHomeData(map);

        // dp_coastal
        // 指标6：沿海岸线人口分布分析
        List<CoastalAreaResponse> data = getCoastalWorkHomePopulationData(map);
        getCoastalActiveAreaData(data, map);

        // dp_city_migrate
        // 指标7: 城市人口迁徙
        getCityMigrationData(map);

        return ReportFtlRender.render(configurer, "report/form-rkdtbhfx.ftl", map);*/
        List<ZkDataMetricEntity> entities = dataDao.selectMetricByIds(Arrays.asList(1L,2L,3L,4L,9L,10L,11L,37L,38L,39L,40L));
        StringBuilder html = new StringBuilder();

//        map.put("title", "人口动态变化分析报告");
//        map.put("pubTime", DateUtil.nowDate());
//        map.put("author", "system");
//        map.put("readCount", 1);

        String headHtml = ReportFtlRender.render(configurer, "report/metric/metric-head.ftl", map);
        String footHtml = ReportFtlRender.render(configurer, "report/metric/metric-footer.ftl", map);
        html.append(headHtml);
        for (int i = 0; i < entities.size(); i++) {
            html.append(generateFromFtl(i+1, entities.get(i).getMetricFtl(), map));
        }
        html.append(footHtml);
        return html.toString();
    }

    private String generateFromFtl(int serialNumber, String template, Map<String, Object> map) {
        map.put("serialNumber", ArabNumber.toChinese(serialNumber));
        METRIC_MAP.get(template).apply(map);
        return ReportFtlRender.render(configurer, "report/metric/" + template + ".ftl", map);
    }

    /**
     * 获取常住人口
     * @param periodEnum 时间段
     */
    @Override
    public Map<String, Object> getPermanentPopulationData(PeriodEnum periodEnum, Map<String, Object> vars) {
        ReportPeriodType thisPeriod;
        ReportPeriodType lastPeriod;

        if (periodEnum == PeriodEnum.WEEK) {
            thisPeriod = ReportPeriodType.THIS_WEEK;
            lastPeriod = ReportPeriodType.LAST_WEEK;
        } else if (periodEnum == PeriodEnum.MONTH) {
            thisPeriod = ReportPeriodType.THIS_MONTH;
            lastPeriod = ReportPeriodType.LAST_MONTH;
        } else if (periodEnum == PeriodEnum.SEASON) {
            thisPeriod = ReportPeriodType.THIS_SEASON;
            lastPeriod = ReportPeriodType.LAST_SEASON;
        } else if (periodEnum == PeriodEnum.YEAR) {
            thisPeriod = ReportPeriodType.THIS_YEAR;
            lastPeriod = ReportPeriodType.LAST_YEAR;
        } else {
            thisPeriod = ReportPeriodType.FIRST_HALF_YEAR;
            lastPeriod = ReportPeriodType.LATTER_HALF_LAST_YEAR;
        }

        List<ZkDataPermanentPopulationEntity> thisPeriodEntities = getAllPermanentPopulationData(thisPeriod);
        List<ZkDataPermanentPopulationEntity> lastPeriodEntities = getAllPermanentPopulationData(lastPeriod);

        Map<String, ZkDataPermanentPopulationEntity> lastPeriodMap = lastPeriodEntities
                .stream().collect(Collectors.toMap(ZkDataPermanentPopulationEntity::getAreaCode, it -> it));

        List<PermanentPopulationResponse> responses = new ArrayList<>();
        thisPeriodEntities.forEach(it -> {
            PermanentPopulationResponse response = new PermanentPopulationResponse();
            ZkDataPermanentPopulationEntity lastE = lastPeriodMap.get(it.getAreaCode());
            long lastCount = 0;
            if (lastE != null && lastE.getCount() > 0) {
                lastCount = lastE.getCount();
                response.setRate(getGrowth(lastE.getCount(), it.getCount()));
            } else {
                response.setRate("100");
            }
            responses.add(response
                    .setCounty(it.getArea())
                    .setAreaCode(it.getAreaCode())
                    .setNext(it.getCount())
                    .setPre(lastCount));
        });

        PermanentPopulationResponse maxRate = responses.stream().max(Comparator.comparing(PermanentPopulationResponse::getRate)).orElseGet(PermanentPopulationResponse::new);
        vars.put("dataTable1", JSON.toJSONString(responses));
        vars.put("dataTable1MaxRate", JSON.toJSONString(maxRate));
        return vars;
    }

    private static final String DESC_FLOW_POP_P2 = "，根据各区域内流动用户数量及分布特征图可看出，{}市流动人口最多，数量高达{}万人，其次是{}市，流动人口数为{}万人，{}市排名第三，流动人口数为{}万人。";
    /**
     * 获取流动人口
     */
    @Override
    public Map<String, Object> getFlowPopulation(Map<String, Object> vars) {
        final List<FlowPopulationResponse> responses = new ArrayList<>();
        String beginDate = (String) vars.get("beginDate");
        String endDate = (String) vars.get("endDate");
        List<ZkDataFlowPopulationEntity> entities = dataDao.selectFlowPopulation(DateTime.now().toString("yyyy-MM-dd 00:00:00"), endDate);
        entities.forEach(it -> {
            responses.add(new FlowPopulationResponse().setCounty(it.getArea()).setCount(it.getCount()));
        });

        long dataTable2Total = responses.stream().mapToLong(FlowPopulationResponse::getCount).sum();
        vars.put("dataTable2", JSON.toJSONString(responses));
        vars.put("dataTable2Total", MathUtil.getTenThousand(dataTable2Total));
        if (CollectionUtils.size(responses) >= 3) {
            String flowPopDescP2 = MessageFormatter.arrayFormat(DESC_FLOW_POP_P2, new String[]{
                    responses.get(0).getCounty(), MathUtil.getTenThousand(responses.get(0).getCount()),
                    responses.get(1).getCounty(), MathUtil.getTenThousand(responses.get(1).getCount()),
                    responses.get(2).getCounty(), MathUtil.getTenThousand(responses.get(2).getCount()),
            }).getMessage();
            vars.put("flowPopDescP2", flowPopDescP2);
        } else {
            vars.put("flowPopDescP2", "");
        }
        return vars;
    }

    /**
     * 获取境外人口
     */
    @Override
    public long getForeignPopulation(Map<String, Object> vars) {
        final List<ForeignPopulationResponse> responses = new ArrayList<>();
        String beginDate = (String) vars.get("beginDate");
        String endDate = (String) vars.get("endDate");
        List<ZkDataForeignPopulationEntity> entities = dataDao.selectForeignPopulation(beginDate, endDate);
        entities.forEach(it -> {
            responses.add(new ForeignPopulationResponse().setCount(it.getCount()).setCountry(it.getCountry()));
        });
        if (entities.size() == 0) {
            return 0;
        }
        long dataTable3Total = responses.stream().mapToLong(ForeignPopulationResponse::getCount).sum();
        vars.put("dataTable3", JSON.toJSONString(responses));
        vars.put("dataTable3Total", dataTable3Total);
        vars.put("dataTable3FirstRate", getRate(responses.get(0).getCount(), dataTable3Total));
        vars.put("dataTable3SecondRate", getRate(responses.get(1).getCount(), dataTable3Total));
        vars.put("maxForeignPopulationCount", String.valueOf(responses.stream().max(Comparator.comparing(ForeignPopulationResponse::getCount)).get().getCount()));
        return dataTable3Total;
    }

    private static final String DESC_FOREIGN_P1 = "截至目前，全省共有境外人员{}人，占全省人口的{}%。其中，{}内境外人口最多，人数高达{}人，占全省境外人口的{}%，其次是{}，超过{}人，{}境外人口超过{}人，{}的境外人口最少，人数仅为{}人，由此可见，境外人口首选地区为{}，其次是{}。";
    /**
     * 获取境外人口（基于市县）
     */
    @Override
    public Map<String, Object> getForeignPopulationByLocalCity(long total, Map<String, Object> vars) {
        // 获取全省人口
        List<ZkDataPermanentPopulationEntity> thisPeriodEntities = getAllPermanentPopulationData(ReportPeriodType.THIS_WEEK);
        long wholeProvPopCount = thisPeriodEntities.stream().mapToLong(ZkDataPermanentPopulationEntity::getCount).sum();

        final List<ForeignPopulationResponse> responses = new ArrayList<>();
        String beginDate = (String) vars.get("beginDate");
        String endDate = (String) vars.get("endDate");
        List<ZkDataForeignPopulationEntity> entities = dataDao.selectForeignPopulationByCity(beginDate, endDate);
        entities.forEach(it -> {
            responses.add(new ForeignPopulationResponse().setCount(it.getCount()).setCountry(it.getArea()));
        });
        if (entities.size() <= 0) {
            return vars;
        }
        vars.put("dataTable4", JSON.toJSONString(responses));
        if (total <= 0) {
            return vars;
        }
        vars.put("dataTable4FirstRate", getRate(responses.get(0).getCount(), total));
        vars.put("dataTable4SecondRate", getRate(responses.get(1).getCount(), total));
        vars.put("foreignDesc", MessageFormatter.arrayFormat(DESC_FOREIGN_P1, new String[]{
                String.valueOf(total), MathUtil.getRate(total, wholeProvPopCount),
                responses.get(0).getCountry(), String.valueOf(responses.get(0).getCount()), MathUtil.getRate(responses.get(0).getCount(), total),
                responses.get(1).getCountry(), String.valueOf(responses.get(1).getCount()),
                responses.get(2).getCountry(), String.valueOf(responses.get(2).getCount()),
                responses.get(responses.size()-1).getCountry(), String.valueOf(responses.get(responses.size()-1).getCount()),
                responses.get(0).getCountry(), responses.get(1).getCountry()
        }).getMessage());
        return vars;
    }

    private boolean belongsHaiKouArea(String areaName) {
        for (String s : new String[]{"龙华区", "美兰区", "琼山区", "秀英区"}) {
            if (s.equals(areaName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 人口地理分布d
     */
    @Override
    public Map<String, Object> getPopulationDistributed(Map<String, Object> map) {
        List<PopulationDistributeResponse> responses = new ArrayList<>();
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<ZkDataActivePopulationEntity> entities = dataDao.selectActivePopulation(beginDate, endDate);
        List<ZkDataActivePopulationEntity> haiKouAreaEntities = entities.stream().filter(o -> belongsHaiKouArea(o.getArea())).collect(Collectors.toList());
        long haiKouAreaCount = 0L;
        for (ZkDataActivePopulationEntity haiKouAreaEntity : haiKouAreaEntities) {
            haiKouAreaCount += haiKouAreaEntity.getCount();
        }
        entities = entities.stream().filter(o -> !belongsHaiKouArea(o.getArea())).collect(Collectors.toList());
        for (ZkDataActivePopulationEntity it : entities) {
            if (!it.getArea().equals("海口市")) {
                responses.add(new PopulationDistributeResponse()
                        .setRegion(HainanAreaUtil.getType(it.getArea()))
                        .setCount(it.getCount())
                        .setCounty(it.getArea()));
            } else {
                responses.add(new PopulationDistributeResponse()
                        .setRegion(HainanAreaUtil.getType(it.getArea()))
                        .setCount(it.getCount() + haiKouAreaCount)
                        .setCounty(it.getArea()));
            }
        }
        long dataTable5Total = responses.stream().mapToLong(PopulationDistributeResponse::getCount).sum();
        Map<String, PopulationDistributeResponse> regionMap= responses.stream().collect(Collectors.toMap(PopulationDistributeResponse::getRegion, o -> o, (a, b) -> {
            a.setCount(a.getCount() + b.getCount());
            a.setCounty(a.getCounty() + "、" + b.getCounty());
            return a;
        }));
        List<PopulationDistributeResponse> populationDistributedReduced = new ArrayList<>();
        regionMap.forEach((k, v) -> populationDistributedReduced.add(v));
        populationDistributedReduced.sort(Comparator.comparing(PopulationDistributeResponse::getCount).reversed());
        map.put("dataTable5", JSON.toJSONString(responses.stream().filter(o -> !belongsHaiKouArea(o.getCounty())).collect(Collectors.toList())));
        map.put("dataTable5Total", dataTable5Total);
        map.put("dataTable5Region", JSON.toJSONString(populationDistributedReduced));
        if (populationDistributedReduced.size() <= 0) {
            return map;
        }
        map.put("dataTable5FirstRegionRate" , getRate(populationDistributedReduced.get(0).getCount(), dataTable5Total));
        map.put("dataTable5SecondRegionRate", getRate(populationDistributedReduced.get(1).getCount(), dataTable5Total));
        map.put("dataTable5ThirdRegionRate" , getRate(populationDistributedReduced.get(2).getCount(), dataTable5Total));
        map.put("dataTable5FourthRegionRate", getRate(populationDistributedReduced.get(3).getCount(), dataTable5Total));
        map.put("dataTable5LastRegionRate"  , getRate(populationDistributedReduced.get(4).getCount(), dataTable5Total));
        return map;
    }

    /**
     * 根据时间范围获取常住人口数据
     * @param periodType 时间范围
     * @return 常驻人口数据
     */
    private List<ZkDataPermanentPopulationEntity> getAllPermanentPopulationData(ReportPeriodType periodType) {
        String[] dr = PeriodUtil.getDateRange(periodType);
        List<ZkDataPermanentPopulationEntity> entities = dataDao.selectByPeriod(dr[0], dr[1]);
        ZkDataPermanentPopulationEntity total = entities.stream().filter(o -> belongsHaiKouArea(o.getArea())).reduce((a, b) -> {
            a.setCount(a.getCount() + b.getCount());
            return a;
        }).orElse(new ZkDataPermanentPopulationEntity());
        List<ZkDataPermanentPopulationEntity> newEntities = new ArrayList<>();
        entities.stream().filter(o -> !belongsHaiKouArea(o.getArea())).forEach(it -> {
            if (!it.getArea().equals("海口市")) {
                newEntities.add(it);
            } else {
                it.setCount(it.getCount() + total.getCount());
                newEntities.add(it);
            }
        });
        return newEntities;
    }

    private static final String DESC_SMU_OD_COUNT_FE = "据统计，省内每天人均出行次数为{}次/人·天，按此标准计算，截至目前，省内居民每天出行量约{}次，按出行次数划分4个区间，各区间人数占比见下表。";

    /**
     * 获取走私人员出行次数
     */
    @Override
    public Map<String, Object> getSmugglerOdTimesData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        ZkDataOdPopulationTimesEntity entity = dataDao.selectLastOdTimesData(beginDate, endDate);
        List<SmugglerOdResponse> responses = new ArrayList<>();
        if (entity == null) {
            entity = new ZkDataOdPopulationTimesEntity().setT1(0L).setT2(0L).setT3(0L).setT4(0L);
        }
        responses.add(new SmugglerOdResponse().setFrequency("0~2次").setCount(entity.getT1()));
        responses.add(new SmugglerOdResponse().setFrequency("3~4次").setCount(entity.getT2()));
        responses.add(new SmugglerOdResponse().setFrequency("5~6次").setCount(entity.getT3()));
        responses.add(new SmugglerOdResponse().setFrequency("7次以上").setCount(entity.getT4()));
        // 出行总人数
        long totalUserCount = responses.stream().mapToLong(SmugglerOdResponse::getCount).sum();
        long totalOdCount = responses.get(0).getCount() * 2 + responses.get(0).getCount() * 4 + responses.get(0).getCount() * 6 + responses.get(0).getCount() * 7;
        map.put("dataTable6", JSON.toJSONString(responses));
        map.put("table6Desc", MessageFormatter.arrayFormat(DESC_SMU_OD_COUNT_FE, new String[]{
                totalOdCount==0?"0":BigDecimal.valueOf(totalOdCount).divide(BigDecimal.valueOf(totalUserCount*7), 0, 2).toPlainString(),
                totalOdCount==0?"0":BigDecimal.valueOf(totalOdCount).divide(BigDecimal.valueOf(7), 0, 2).toPlainString()
        }).getMessage());
        return map;
    }

    /**
     * 获取走私人员出行次数
     */
    @Override
    public Map<String, Object> getSmugglerOdPeriodData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        ZkDataOdPopulationPeriodEntity entity = dataDao.selectLastOdPeriodData(beginDate, endDate);
        if (entity == null) {
            entity = new ZkDataOdPopulationPeriodEntity();
        }
        List<SmugglerOdPeriodResponse> responses = new ArrayList<>();
        responses.add(new SmugglerOdPeriodResponse().setTime("23-24点时间段").setTimeX("00:00").setCount(entity.getC23()));
        responses.add(new SmugglerOdPeriodResponse().setTime("0-1点时间段").setTimeX("01:00").setCount(entity.getC0()));
        responses.add(new SmugglerOdPeriodResponse().setTime("1-2点时间段").setTimeX("02:00").setCount(entity.getC1()));
        responses.add(new SmugglerOdPeriodResponse().setTime("2-3点时间段").setTimeX("03:00").setCount(entity.getC2()));
        responses.add(new SmugglerOdPeriodResponse().setTime("3-4点时间段").setTimeX("04:00").setCount(entity.getC3()));
        responses.add(new SmugglerOdPeriodResponse().setTime("4-5点时间段").setTimeX("05:00").setCount(entity.getC4()));
        responses.add(new SmugglerOdPeriodResponse().setTime("5-6点时间段").setTimeX("06:00").setCount(entity.getC5()));
        responses.add(new SmugglerOdPeriodResponse().setTime("6-7点时间段").setTimeX("07:00").setCount(entity.getC6()));
        responses.add(new SmugglerOdPeriodResponse().setTime("7-8点时间段").setTimeX("08:00").setCount(entity.getC7()));
        responses.add(new SmugglerOdPeriodResponse().setTime("8-9点时间段").setTimeX("09:00").setCount(entity.getC8()));
        responses.add(new SmugglerOdPeriodResponse().setTime("9-10点时间段").setTimeX("10:00").setCount(entity.getC9()));
        responses.add(new SmugglerOdPeriodResponse().setTime("10-11点时间段").setTimeX("11:00").setCount(entity.getC10()));
        responses.add(new SmugglerOdPeriodResponse().setTime("11-12点时间段").setTimeX("12:00").setCount(entity.getC11()));
        responses.add(new SmugglerOdPeriodResponse().setTime("12-13点时间段").setTimeX("13:00").setCount(entity.getC12()));
        responses.add(new SmugglerOdPeriodResponse().setTime("13-14点时间段").setTimeX("14:00").setCount(entity.getC13()));
        responses.add(new SmugglerOdPeriodResponse().setTime("14-15点时间段").setTimeX("15:00").setCount(entity.getC14()));
        responses.add(new SmugglerOdPeriodResponse().setTime("15-16点时间段").setTimeX("16:00").setCount(entity.getC15()));
        responses.add(new SmugglerOdPeriodResponse().setTime("16-17点时间段").setTimeX("17:00").setCount(entity.getC16()));
        responses.add(new SmugglerOdPeriodResponse().setTime("17-18点时间段").setTimeX("18:00").setCount(entity.getC17()));
        responses.add(new SmugglerOdPeriodResponse().setTime("18-19点时间段").setTimeX("19:00").setCount(entity.getC18()));
        responses.add(new SmugglerOdPeriodResponse().setTime("19-20点时间段").setTimeX("20:00").setCount(entity.getC19()));
        responses.add(new SmugglerOdPeriodResponse().setTime("20-21点时间段").setTimeX("21:00").setCount(entity.getC20()));
        responses.add(new SmugglerOdPeriodResponse().setTime("21-22点时间段").setTimeX("22:00").setCount(entity.getC21()));
        responses.add(new SmugglerOdPeriodResponse().setTime("22-23点时间段").setTimeX("23:00").setCount(entity.getC22()));
        map.put("dataTable7", JSON.toJSONString(responses));
        responses.sort(Comparator.comparing(SmugglerOdPeriodResponse::getCount).reversed());
        long smugglerOdPeriodDataTotal = responses.stream().mapToLong(SmugglerOdPeriodResponse::getCount).sum();
        map.put("dataTable7Sorted", JSON.toJSONString(responses));
        map.put("dataTable7FirstRegion", getRate(responses.get(0).getCount(), smugglerOdPeriodDataTotal));
        map.put("dataTable7SecondRegion", getRate(responses.get(1).getCount(), smugglerOdPeriodDataTotal));
        return map;
    }

    private static final String DESC_SMU_OD_DISTANCE = "据统计，平均出行距离在{}的人数占比最多，占总出行人数的{}%，平均出行距离在50KM以上的人数为{}人，占总出行人数的{}%。";

    /**
     * 走私人员出行距离分布特征
     */
    @Override
    public Map<String, Object> getSmugglerOdDistanceData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        ZkDataOdPopulationDistanceEntity entity = dataDao.selectLastOdDistanceData(beginDate, endDate);
        if (entity == null) {
            entity = new ZkDataOdPopulationDistanceEntity();
        }
        List<SmugglerOdPeriodResponse> responses = new ArrayList<>();
        /*responses.add(new SmugglerOdPeriodResponse().setTime("平均出行距离在3KM以内").setCount(entity.getD1()));
        responses.add(new SmugglerOdPeriodResponse().setTime("平均出行距离在3~5KM").setCount(entity.getD2()));
        responses.add(new SmugglerOdPeriodResponse().setTime("平均出行距离在5~15KM").setCount(entity.getD3()));
        responses.add(new SmugglerOdPeriodResponse().setTime("平均出行距离在15~30KM").setCount(entity.getD4()));
        responses.add(new SmugglerOdPeriodResponse().setTime("平均出行距离在30~50KM").setCount(entity.getD5()));
        responses.add(new SmugglerOdPeriodResponse().setTime("平均出行距离在50KM以上").setCount(entity.getD6()));*/
        responses.add(new SmugglerOdPeriodResponse().setTime("0~3KM").setCount(entity.getD1()));
        responses.add(new SmugglerOdPeriodResponse().setTime("3~5KM").setCount(entity.getD2()));
        responses.add(new SmugglerOdPeriodResponse().setTime("5~15KM").setCount(entity.getD3()));
        responses.add(new SmugglerOdPeriodResponse().setTime("15~30KM").setCount(entity.getD4()));
        responses.add(new SmugglerOdPeriodResponse().setTime("30~50KM").setCount(entity.getD5()));
        responses.add(new SmugglerOdPeriodResponse().setTime("50KM以上").setCount(entity.getD6()));
        map.put("dataTable8", JSON.toJSONString(responses));
        long smuggerOdDistanceDataTotal = responses.stream().mapToLong(SmugglerOdPeriodResponse::getCount).sum();
        map.put("dataTable8FirstRate", getRate(responses.get(0).getCount(), smuggerOdDistanceDataTotal));
        map.put("dataTable8LastRate", getRate(responses.get(responses.size()-1).getCount(), smuggerOdDistanceDataTotal));
        //responses.sort(Comparator.comparing(SmugglerOdPeriodResponse::getCount).reversed());
        map.put("dataTable8Sorted", JSON.toJSONString(responses));
        SmugglerOdPeriodResponse maxResponse = responses.stream().max(Comparator.comparing(SmugglerOdPeriodResponse::getCount)).get();
        map.put("dataTable8Desc", MessageFormatter.arrayFormat(DESC_SMU_OD_DISTANCE, new String[]{
                maxResponse.getTime(),
                MathUtil.getRate(maxResponse.getCount(), smuggerOdDistanceDataTotal),
                String.valueOf(responses.get(5).getCount()),
                MathUtil.getRate(responses.get(5).getCount(), smuggerOdDistanceDataTotal)
        }).getMessage());
        return map;
    }

    private static final String DESC_SMU_OD_ARRIVE_AREA = "出行距离主要反映出行的行程长短，与出行目的和城市布局等诸多因素有关，不同出行目的和交通方式的出行距离不同。据统计结果可得，由{}作为出发点的出行人数最多，以{}作为目的地的出行人数最多。";
    /**
     * 走私人员出发地分布特征, 走私人员到达地分布特征
     */
    @Override
    public Map<String, Object> getSmugglerOdArriveAreaData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<TravelAreaDto> entities = dataDao.selectSmugglerArriveArea(beginDate, endDate);
        // 到达地
        List<SmugglerAreaPopulationResponse> startAreaResponses = entities.stream()
                .filter(o -> o.getType() == 1 && !AreaUtil.belongsHaiKouArea(o.getAreaCounty()))
                .map(o -> new SmugglerAreaPopulationResponse()
                        .setCounty(o.getAreaCounty())
                        .setCount(o.getCount())
                )
                .sorted(Comparator.comparing(SmugglerAreaPopulationResponse::getCount).reversed())
                .collect(Collectors.toList());
        // 出发地
        List<SmugglerAreaPopulationResponse> arrivedAreaResponses = entities.stream()
                .filter(o -> o.getType() == 0 && !AreaUtil.belongsHaiKouArea(o.getAreaCounty()))
                .map(o -> new SmugglerAreaPopulationResponse()
                        .setCounty(o.getAreaCounty())
                        .setCount(o.getCount())
                )
                .sorted(Comparator.comparing(SmugglerAreaPopulationResponse::getCount).reversed())
                .collect(Collectors.toList());

        map.put("smuOdStartAreaData", JSON.toJSONString(startAreaResponses));
        map.put("smuOdArrivedAreaData", JSON.toJSONString(arrivedAreaResponses));
        if (startAreaResponses.size() > 0 && arrivedAreaResponses.size() > 0) {
            map.put("smuOdArriveAreaDesc", MessageFormatter.arrayFormat(DESC_SMU_OD_ARRIVE_AREA, new String[]{
                    startAreaResponses.get(0).getCounty(),
                    arrivedAreaResponses.get(0).getCounty()
            }).getMessage());
        } else {
            map.put("smuOdArriveAreaDesc", "");
        }
        return map;
    }

    private static final String DESC_SMU_OD_WORK_HOME = "由图6和图7可看出，居住在{}区的人数最多，高达{}人，工作在{}区的人数最多，一共有{}人，工作与居住地重合度最高的地区为{}区，居住人数为{}人，工作人数为{}人。";
    /**
     * 走私人员居住地分布特征, 走私人员工作地分布特征
     */
    @Override
    public Map<String, Object> getSmugglerWorkHomeData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<ZkDataSmugglerWorkHomeEntity> entities = dataDao.selectSmugglerWorkHomeData(beginDate, endDate);
        List<SmugglerAreaPopulationResponse> workResponses = new ArrayList<>();
        List<SmugglerAreaPopulationResponse> homeResponses = new ArrayList<>();
        entities.forEach(it -> {
            workResponses.add(new SmugglerAreaPopulationResponse().setCounty(it.getArea()).setCount(it.getWorkCount()));
            homeResponses.add(new SmugglerAreaPopulationResponse().setCounty(it.getArea()).setCount(it.getHomeCount()));
        });
        workResponses.sort(Comparator.comparing(SmugglerAreaPopulationResponse::getCount).reversed());
        homeResponses.sort(Comparator.comparing(SmugglerAreaPopulationResponse::getCount).reversed());
        map.put("smuOdWorkData", JSON.toJSONString(workResponses));
        map.put("smuOdHomeData", JSON.toJSONString(homeResponses));
        ZkDataSmugglerWorkHomeEntity maxEntity = new ZkDataSmugglerWorkHomeEntity();
        if (entities.size() > 0) {
            maxEntity = entities.stream().max(Comparator.comparing(o -> o.getWorkCount() + o.getHomeCount())).get();
        }
        String smuOdWorkHomeDesc = "";
        if (workResponses.size() > 0 && homeResponses.size() > 0) {
            smuOdWorkHomeDesc = MessageFormatter.arrayFormat(DESC_SMU_OD_WORK_HOME, new String[]{
                    homeResponses.get(0).getCounty(), String.valueOf(homeResponses.get(0).getCount()),
                    workResponses.get(0).getCounty(), String.valueOf(workResponses.get(0).getCount()),
                    maxEntity.getArea(),
                    String.valueOf(maxEntity.getHomeCount()),
                    String.valueOf(maxEntity.getWorkCount())
            }).getMessage();
        }
        map.put("smuOdWorkHomeDesc", smuOdWorkHomeDesc);
        return map;
    }

    private static final String DESC_DP_COASTAL_POP = "截至目前统计，沿海岸线常住人口居住地分布主要集中于于{}区，人口数高达{}人，占沿海岸线总人口数的{}%，工作地分布主要集中于{}区，人口数高达{}人，占沿海岸线总人口数的{}%";
    private static final String DESC_DP_COASTAL_POP2 = "沿海岸线居民常规活动区域集中于{}区";
    /**
     * 沿海职住人口分布特征
     */
    @Override
    public List<CoastalAreaResponse> getCoastalWorkHomePopulationData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<ZkDataCoastalPopulationWorkHomeEntity> entities = dataDao.selectCoastalWorkHomeData(beginDate, endDate);
        List<CoastalAreaResponse> workResponse = new ArrayList<>();
        List<CoastalAreaResponse> homeResponse = new ArrayList<>();
        List<CoastalAreaResponse> allResponse = new ArrayList<>();
        entities.forEach(it -> {
            workResponse.add(new CoastalAreaResponse().setCount(it.getWorkCount()).setCounty(it.getArea()));
            homeResponse.add(new CoastalAreaResponse().setCount(it.getHomeCount()).setCounty(it.getArea()));
            allResponse.add(new CoastalAreaResponse().setCount(it.getWorkCount()+it.getHomeCount()).setCounty(it.getArea()));
        });
        workResponse.sort(Comparator.comparing(CoastalAreaResponse::getCount).reversed());
        homeResponse.sort(Comparator.comparing(CoastalAreaResponse::getCount).reversed());
        allResponse.sort(Comparator.comparing(CoastalAreaResponse::getCount).reversed());
        long coastalHomeTotal = homeResponse.stream().mapToLong(CoastalAreaResponse::getCount).sum();
        long coastalWorkTotal = workResponse.stream().mapToLong(CoastalAreaResponse::getCount).sum();
        map.put("dataTable13", JSON.toJSONString(homeResponse));
        map.put("dataTable14", JSON.toJSONString(workResponse));
        if (homeResponse.size() == 0) {
            homeResponse.add(new CoastalAreaResponse());
        }
        if (workResponse.size() == 0) {
            workResponse.add(new CoastalAreaResponse());
        }
        map.put("dataTable13FirstRate", getRate(homeResponse.get(0).getCount(), coastalHomeTotal));
        map.put("dataTable14FirstRate", getRate(workResponse.get(0).getCount(), coastalWorkTotal));
        map.put("coastalPopDesc", MessageFormatter.arrayFormat(DESC_DP_COASTAL_POP, new String[]{
            coastalHomeTotal==0?"无":homeResponse.get(0).getCounty(), String.valueOf(coastalHomeTotal), MathUtil.getRate(coastalHomeTotal, coastalHomeTotal+coastalWorkTotal),
            coastalWorkTotal==0?"无":workResponse.get(0).getCounty(), String.valueOf(coastalWorkTotal), MathUtil.getRate(coastalWorkTotal, coastalHomeTotal+coastalWorkTotal)
        }).getMessage());
        return allResponse;
    }

    /**
     * 沿海活跃地区
     */
    @Override
    public Map<String, Object> getCoastalActiveAreaData(List<CoastalAreaResponse> responses, Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<ZkDataCoastalActiveAreaEntity> entities = dataDao.selectCoastalActiveAreaData(beginDate, endDate);
        List<CoastalAreaResponse> list = new ArrayList<>();
        entities.forEach(it -> list.add(new CoastalAreaResponse().setCounty(it.getArea()).setCount(it.getCount())));
        map.put("dataTable15", JSON.toJSONString(list));
        List<CoastAreaMapResponse> coastAreaMapResponses = new ArrayList<>();
        responses.forEach(it -> coastAreaMapResponses.add(new CoastAreaMapResponse().setPubArea(it.getCounty()).setPubCount(it.getCount())));
        map.put("dataTable15Map", JSON.toJSONString(coastAreaMapResponses));
        if (list.size() > 0) {
            Optional<CoastalAreaResponse> max = list.stream().max(Comparator.comparing(CoastalAreaResponse::getCount));
            String s = MessageFormatter.format(DESC_DP_COASTAL_POP2, max.get().getCounty()).getMessage();
            String coastalPopDesc = map.get("coastalPopDesc") + "，" + s;
            map.put("coastalPopDesc", coastalPopDesc);
        }
        return map;
    }

    /**
     * 城市迁徙
     */
    @Override
    public Map<String, Object> getCityMigrationData(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<ZkDataPopulationMigrateEntity> entities = dataDao.selectMigrationData(beginDate, endDate);
        entities = entities.stream().filter(o -> o.getState() == 3).collect(Collectors.toList());
        List<CityPopulationMigrationResponse> inResponses = new ArrayList<>();
        List<CityPopulationMigrationResponse> outResponses = new ArrayList<>();
        entities.forEach(it -> {
            inResponses.add(new CityPopulationMigrationResponse()
                    .setCount(it.getCount())
                    .setPreCounty(it.getToArea())
                    .setCounty(it.getFromArea()));
            outResponses.add(new CityPopulationMigrationResponse()
                    .setCount(it.getCount())
                    .setPreCounty(it.getFromArea())
                    .setCounty(it.getToArea()));
        });

        List<CityPopulationMigrationResponse> inResponseMerged = mergeMigrationDataResponse(inResponses);
        List<CityPopulationMigrationResponse> outResponseMerged = mergeMigrationDataResponse(outResponses);
        inResponses.addAll(inResponseMerged);
        outResponses.addAll(outResponseMerged);

        inResponses.sort(Comparator.comparing(CityPopulationMigrationResponse::getPreCounty).thenComparing(CityPopulationMigrationResponse::getCount));
        outResponses.sort(Comparator.comparing(CityPopulationMigrationResponse::getPreCounty).thenComparing(CityPopulationMigrationResponse::getCount));

        inResponseMerged.sort(Comparator.comparing(CityPopulationMigrationResponse::getCount).reversed());
        outResponseMerged.sort(Comparator.comparing(CityPopulationMigrationResponse::getCount).reversed());

        map.put("dataTable16", JSON.toJSONString(inResponses));
        map.put("dataTable17", JSON.toJSONString(outResponses));

        map.put("dataTable16Merged", JSON.toJSONString(inResponseMerged));
        long dataTable16Total = inResponseMerged.stream().mapToLong(CityPopulationMigrationResponse::getCount).sum();
        if (inResponseMerged.size() == 0) {
            inResponseMerged.add(new CityPopulationMigrationResponse());
            inResponseMerged.add(new CityPopulationMigrationResponse());
            inResponseMerged.add(new CityPopulationMigrationResponse());
        }
        map.put("dataTable16FirstRate", getRate(inResponseMerged.get(0).getCount(), dataTable16Total));
        map.put("dataTable16SecondRate", getRate(inResponseMerged.get(1).getCount(), dataTable16Total));
        map.put("dataTable16ThirdRate", getRate(inResponseMerged.get(2).getCount(), dataTable16Total));

        map.put("dataTable17Merged", JSON.toJSONString(outResponseMerged));
        long dataTable17Total = outResponseMerged.stream().mapToLong(CityPopulationMigrationResponse::getCount).sum();
        if (outResponseMerged.size() == 0) {
            outResponseMerged.add(new CityPopulationMigrationResponse());
            outResponseMerged.add(new CityPopulationMigrationResponse());
            outResponseMerged.add(new CityPopulationMigrationResponse());
        }
        map.put("dataTable17FirstRate", getRate(outResponseMerged.get(0).getCount(), dataTable17Total));
        map.put("dataTable17SecondRate", getRate(outResponseMerged.get(1).getCount(), dataTable17Total));
        map.put("dataTable17ThirdRate", getRate(outResponseMerged.get(2).getCount(), dataTable17Total));
        return map;
    }

    @Override
    public Map<String, Object> getRealTimeUser(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<ZkDataActivePopulationEntity> entities = dataDao.selectActivePopulation(beginDate, endDate);
        map.put("realTimeUser", JSON.toJSONString(entities));
        map.put("realTimeUserTotal", entities.stream().mapToLong(ZkDataActivePopulationEntity::getCount).sum());
        if (entities.size() > 0) {
            map.put("realTimeUserMax", entities.stream().max(Comparator.comparing(ZkDataActivePopulationEntity::getCount)).get().getCount());
        } else {
            map.put("realTimeUserMax", 0);
        }
        return map;
    }

    @Override
    public Map<String, Object> getInOutIslandUser(Map<String, Object> map) {
        String beginDate = (String) map.get("beginDate");
        String endDate = (String) map.get("endDate");
        List<DateWithCountDto> abroadFlowIntoDtoList = dataDao.selectAbroadFlowIntoUser(beginDate, endDate);
        List<DateWithCountDto> outIslandDomesticFlowIntoDtoList = dataDao.selectDomesticFlowIntoUser(beginDate, endDate, 1);
        List<DateWithCountDto> inIslandDomesticFlowIntoDtoList = dataDao.selectDomesticFlowIntoUser(beginDate, endDate, 0);
        map.put("abroadPopulationFlowInto", JSON.toJSONString(abroadFlowIntoDtoList));
        map.put("outIslandPopulationFlowInto", JSON.toJSONString(outIslandDomesticFlowIntoDtoList));
        map.put("inIslandPopulationFlowInto", JSON.toJSONString(inIslandDomesticFlowIntoDtoList));
        return map;
    }

    @Override
    public Map<String, Object> getPopulationStructure(Map<String, Object> map) {
        List<S02PopuAgeStruEntity> ageStructureDataList = sgSqBiz.getAgeData();
        map.put("ageStructureDataList", JSON.toJSONString(ageStructureDataList));
        return map;
    }

    @Override
    public Map<String, Object> getAreaJobStructure(Map<String, Object> map) {
        // FIXED DATA
        return map;
    }

    private List<CityPopulationMigrationResponse> mergeMigrationDataResponse(List<CityPopulationMigrationResponse> responses) {
        return new ArrayList<>(responses.stream().collect(Collectors.toMap(CityPopulationMigrationResponse::getPreCounty, o -> new CityPopulationMigrationResponse().setCounty(o.getCounty()).setCount(o.getCount()).setPreCounty(o.getPreCounty()).setCounty(TOTAL_FIELD_NAME), (a, b) -> a.setCount(a.getCount() + b.getCount()))).values());
    }


    /**
     * 计算：增长率百分数 = (b-a) / a * 100
     * @param a a
     * @param b b
     * @return 增长率百分数
     */
    private String getGrowth(Long a, Long b) {
        final BigDecimal aD = BigDecimal.valueOf(a);
        final BigDecimal bD = BigDecimal.valueOf(b);
        return bD.subtract(aD).divide(bD, 4, BigDecimal.ROUND_DOWN)
                .multiply(HUNDRED_DECIMAL).setScale(2, BigDecimal.ROUND_DOWN).toPlainString();
    }

    private String getRate(Long a, Long b) {
        if (b == 0) {
            return "0";
        }
        final BigDecimal aD = BigDecimal.valueOf(a);
        final BigDecimal bD = BigDecimal.valueOf(b);
        return aD.divide(bD, 4, BigDecimal.ROUND_DOWN)
                .multiply(HUNDRED_DECIMAL).setScale(2, BigDecimal.ROUND_DOWN).toPlainString();
    }
}
